﻿using System;
using System.Collections.Concurrent;
using System.Threading;

namespace Micro.Extensions.Log
{
    public  class MicroLoggerProcessor: IDisposable
    {
        private readonly Thread _outputThread;
        private readonly IFileDiskWriter _fileWriter;
        private readonly BlockingCollection<BaseMessage> _messageQueue;
        private readonly ILoggerWriter _loggerWriter;

        public MicroLoggerProcessor(ILoggerWriter logWriter, IFileDiskWriter fileWriter, int maxQueuedMessageCount)
        {
             _loggerWriter = logWriter ?? new ConsoleLogWriter(); 

            _fileWriter = fileWriter;
            _messageQueue = new BlockingCollection<BaseMessage>(maxQueuedMessageCount);

            _outputThread = new Thread(ProcessLogQueue)
            {
                IsBackground = true,
                Name = "Console logger queue processing thread"
            };
            _outputThread.Start();
        }

        public virtual void EnqueueMessage(BaseMessage message)
        {
            if (!_messageQueue.TryAdd(message))
            {
                //messageQueue队列爆满了
                _fileWriter?.Write(new HardDiskInfo(message));
            }
        }

        private void ProcessLogQueue()
        {
            try
            {
                foreach (var message in _messageQueue.GetConsumingEnumerable())
                {
                    WriteMessage(message);
                }
            }
            catch
            {
                try
                {
                    _messageQueue.CompleteAdding();
                }
                catch
                {
                    // ignored
                }
            }
        }

        private void WriteMessage(BaseMessage message)
        {
            try
            {
                _loggerWriter.Write(message);
            }
            catch (Exception e)
            {
                var errMsg = new BaseMessage
                {
                    LogLevel = "fail",
                    LogName = nameof(MicroLoggerProcessor),
                    HostIPAddress = message.HostIPAddress,
                    LogTime = message.LogTime,
                    EventId = message.EventId,
                    ChainId = message.ChainId,
                    ApplicationId = message.ApplicationId,
                    Exception = e.ToString(),
                    LogType = message.LogType,
                    ParentTrackId = message.ParentTrackId,
                    ThreadId = Thread.CurrentThread.ManagedThreadId,
                    ThreadName = Thread.CurrentThread.Name,
                    TrackId = message.TrackId,
                    Message = $"写入Kafka错误，原始 Message：{message.Message}，原始 Exception：{message.Exception}"
                };
                _fileWriter?.Write(new HardDiskInfo { Message = errMsg, FileName = LogConst.ErrorLog });
            }
        }

        public void Dispose()
        {
            try
            {
                // with timeout in-case Console is locked by user input
                _outputThread.Join(1500);
            }
            catch (ThreadStateException) { }

            _messageQueue.Dispose();
            _fileWriter.Dispose();
        }
    }
}
